using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace ACM.cmsarea08.Controls
{
    public class CaptchaImage
    {
        private int _h;
        private int _w;
        private Random _rand;
        private DateTime _startat;
        private string _testocasuale;
        private int _lungtesto;
        private string _caratteritesto;
        private string _carattere;
        private LivelloDistorsione _disto;
        private LivelloDisturboBG _distlev;
        private LivelloDisturboLN _distlev2;
        private string _unico;
        private string _fontdisponibili;

        public enum LivelloDistorsione {
            None,
            Low,
            Medium,
            High,
            Extreme
        }
        public enum LivelloDisturboBG {
            None,
            Low,
            Medium,
            High,
            Extreme
        }
        public enum LivelloDisturboLN {
            None,
            Low,
            Medium,
            High,
            Extreme
        }

        #region Propriet_Esterne
        public string UniqueID
        {
            get { return _unico; }
        }
        public DateTime GeneratedAt
        {
            get { return _startat; }
        }
        public string Font {
            get { return _carattere; }
            set {
                try {
                Font test = new Font(value,12.0F);
                _carattere = value;
                test.Dispose();
                } catch {
                    _carattere = FontFamily.GenericSansSerif.Name;
                }
            }
        }
        public LivelloDistorsione Noise {
            get { return _disto; }
            set { _disto = value; }
        }
        public LivelloDisturboBG BackgroundNoise {
            get { return _distlev; }
            set { _distlev = value;}
        }
        public LivelloDisturboLN LinearNoise {
            get { return _distlev2;}
            set { _distlev2 = value; }
        }
        public string CaptchaChars {
            get { return _caratteritesto; }
            set { 
                _caratteritesto = value;
                _testocasuale = GeneraTesto();
            }
        }
        public int TextLength {
            get { return _lungtesto; }
            set {
                _lungtesto = value;
                _testocasuale = GeneraTesto(); 
            }
        }
        public string Text {
            get { return _testocasuale; }
        }
        public int Width {
            get { return _w; }
            set {
                if(value<=60){
                    //errore ci fu
                    throw new Exception(string.Format(Risorse.ControlsResource.CaptchaImage_WidthError,value));
                }
                
                _w = value;
            }
        }
        public int Heigth {
            get { return _h; }
            set {
                if(value<=30){
                    //errore ci fu
                    throw new Exception(string.Format(Risorse.ControlsResource.CaptchaImage_HeigthError, value));
                }
                _h = value;
            }
        }
        public string FontList {
            get {  return _fontdisponibili; }
            set { _fontdisponibili = value; }

        }
        #endregion


        public CaptchaImage()
        {
            _rand = new Random();
            _disto = LivelloDistorsione.Low;
            _distlev = LivelloDisturboBG.Low;
            _distlev2 = LivelloDisturboLN.None;
            _w = 180;
            _h = 50;
            _lungtesto = 5;
            _caratteritesto = "ACDEFGHJKLNPQRTUVXYZ2346789";
            _carattere ="";
            _fontdisponibili =  "arial;arial black;comic sans ms;courier new;estrangelo edessa;franklin gothic medium;" +
                                "georgia;lucida console;lucida sans unicode;mangal;microsoft sans serif;palatino linotype;" +
                                "sylfaen;tahoma;times new roman;trebuchet ms;verdana";
            _testocasuale = GeneraTesto();
            _startat = DateTime.Now;
            _unico = Guid.NewGuid().ToString();
        }
   
    
        public Bitmap GetImage() {
            return preparaImg();
        }
    
        private string FontRandom() {
            string[] _famiglie = null;
            if(_famiglie==null)
                _famiglie = _fontdisponibili.Split(';');
            return _famiglie[_rand.Next(0,_famiglie.Length)];
        }
        private string GeneraTesto() {
            System.Text.StringBuilder sb = new System.Text.StringBuilder(_lungtesto);
            int ml = _caratteritesto.Length;
            for(int n=0; n<_lungtesto;n++)
                sb.Append(_caratteritesto.Substring(_rand.Next(ml),1));
            return sb.ToString();
        }


//Sezione per la grafica:

        private PointF PuntoACaso(int xmin, int xmax, int ymin, int ymax) {
            return new PointF(_rand.Next(xmin,xmax),_rand.Next(ymin,ymax));
        }
        private PointF PuntoACaso(Rectangle retto) {
            return PuntoACaso(retto.Left, retto.Width, retto.Top, retto.Bottom);
        }
        //Elemento Testo:
        private GraphicsPath ElementoTesto(string s, Font f, Rectangle r) {
            StringFormat sf = new StringFormat();
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Near;
            GraphicsPath g = new GraphicsPath();
            g.AddString(s,f.FontFamily,(int)f.Style,f.Size,r,sf);
            return g;
        }
        private Font OttimizzaFont() {
            Single fsize=0;
            string fnome = _carattere;
            if(fnome=="")
                fnome = this.FontRandom();
            switch (this.Noise) {
                case LivelloDistorsione.None:
                    fsize = Convert.ToInt32(_h*0.7);break;
                case LivelloDistorsione.Low:
                    fsize = Convert.ToInt32(_h*0.8);break;
                case LivelloDistorsione.Medium:
                    fsize = Convert.ToInt32(_h*0.85);break;
                case LivelloDistorsione.High:
                    fsize = Convert.ToInt32(_h*0.9);break;
                case LivelloDistorsione.Extreme:
                    fsize = Convert.ToInt32(_h*0.95);break;
            }
            return new Font(fnome,fsize,FontStyle.Bold);

        }

    
        private Bitmap preparaImg() {
            Font fn = null;
            Rectangle retto;
            Brush penna;
            Bitmap bmp = new Bitmap(_w,_h,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            Graphics gr = System.Drawing.Graphics.FromImage(bmp);
            gr.SmoothingMode = SmoothingMode.AntiAlias;
            //prepara il rettangolo base.
            retto = new Rectangle(0,0,_w,_h);
            penna = new SolidBrush(Color.LightBlue);
            gr.FillRectangle(penna,retto);
            int carpos = 0;
            double lungtesto = _w / _lungtesto;
            Rectangle testoretto;
            foreach(char c in _testocasuale) {
                fn = OttimizzaFont();
                testoretto = new Rectangle(Convert.ToInt32(carpos*lungtesto),0,Convert.ToInt32(lungtesto),_h);
                GraphicsPath gp = ElementoTesto(c.ToString(),fn,testoretto);
                //distorce testo.

                //inserisco il carattere nell'insieme.
                penna = new SolidBrush(Color.Navy);
                gr.FillPath(penna,gp);
                carpos+=1;
            }
            //Aggiungi Disturbo BK,
            AggiungiDisturboBG(gr, retto);
            //Aggiungi Disturbo Lineare,
            AggiungiDisturboLineare(gr, retto);
            //completa e ritorna.
            fn.Dispose();
            penna.Dispose();
            gr.Dispose();
            return bmp;
        }

        private void AggiungiDisturboBG(Graphics gr, Rectangle retto) {
            int qta = 0;
            int dim = 0;
            switch (_distlev) {
                case LivelloDisturboBG.None:
                    return;
                case LivelloDisturboBG.Low:
                    qta = 30; dim = 40; break;
                case LivelloDisturboBG.Medium:
                    qta = 18; dim = 40; break;
                case LivelloDisturboBG.High:
                    qta = 16; dim = 39; break;
                case LivelloDisturboBG.Extreme:
                    qta = 12; dim = 38; break;
            }
            Brush penna = new SolidBrush(Color.Black);
            int mass = Convert.ToInt32(Math.Max(retto.Width,retto.Height) / dim);
            for (int i = 0; i < Convert.ToInt32(retto.Width * retto.Height) / qta;i++ )
            {
                gr.FillEllipse(penna, _rand.Next(retto.Width), _rand.Next(retto.Height),
                    _rand.Next(mass), _rand.Next(mass));
            }
            penna.Dispose();
        }
        private void AggiungiDisturboLineare(Graphics gr, Rectangle retto)
        {
            int len = 0;
            int larg = 0;
            int linee = 0;
            switch (_distlev2)
            {
                case LivelloDisturboLN.None:
                    return;
                case LivelloDisturboLN.Low:
                    len = 4; larg = Convert.ToInt32( (float)_h / 31.25);
                    linee = 1;
                    break;
                case LivelloDisturboLN.Medium:
                    len = 5; larg = Convert.ToInt32(_h / 27.7777);
                    linee = 1;
                    break;
                case LivelloDisturboLN.High:
                    len = 3; larg = Convert.ToInt32(_h / 25);
                    linee = 2;
                    break;
                case LivelloDisturboLN.Extreme:
                    len = 3; larg = Convert.ToInt32(_h / 22.7272);
                    linee = 3;
                    break;
            }
            PointF[] pf = new PointF[len];
            Pen p = new Pen(Color.Black, larg);
            for (int i = 0; i < linee; i++)
            {
                for (int c = 0; c < len; c++)
                    pf[c] = PuntoACaso(retto);
                gr.DrawCurve(p, pf, (float)1.75);
            }
            p.Dispose();
        }
    }
}
